/* 简介：cothread 是一个轻量级协程调度器，由纯C语言实现，易于移植到各种单片机。
 * 同时，由于该调度器仅仅运行在一个实际线程中，所以它也适用于服务器高并发场景。
 *
 * 版本: 1.0.0   2019/02/25
 *
 * 作者: 覃攀 <qinpan1003@qq.com>
 *
 */

#include <stdarg.h>
#include "rtos.h"

static ccb_t *ccb_log = NULL;
static int log_head = 0;
static int log_tail = 0;
static char log_buff[LOG_BUFFER_SIZE];

void wakeup_log_thread(void)
{
    if (ccb_log != NULL && log_head != log_tail)
        thread_signal(ccb_log, OsEvent(1));
}

static coresult_t log_thread(ccb_t *ccb)
{
    char ch;

    thread_start();

    while (1)
    {
        thread_wait(OsEvent(1), 0);
        OsEventClr(1);
        
        while (log_head != log_tail && write_completed())
        {
            ch = log_buff[log_tail];
            if (++log_tail >= LOG_BUFFER_SIZE)
                log_tail = 0;
            
            write_ch(ch);
        }
    }

    thread_end();
}

static unsigned char __insert_ch(char ch)
{
    unsigned int log_head_old = log_head;
    unsigned int log_head_new = log_head_old + 1;

    if (log_head_new >= LOG_BUFFER_SIZE)
        log_head_new = 0;

    if (log_head_new == log_tail)
        return 1;

    log_buff[log_head_old] = ch;
    log_head = log_head_new;

    return 0;
}

static unsigned char insert_ch(char ch)
{
    if (ch != '\n')
        return __insert_ch(ch);

    if (__insert_ch('\r'))
        return 1;

    if (__insert_ch('\n'))
        return 1;
    
    return 0;
}

void colog_normal(char *fmt, ...)
{
    int i;
    va_list argptr;
    char buff[LOG_BUFFER_SIZE];
    int count = sizeof(buff) - 1;
    int log_count = log_head - log_tail;
    
    if (log_count < 0)
        log_count += LOG_BUFFER_SIZE;

    if (count > (LOG_BUFFER_SIZE - log_count))
        count = LOG_BUFFER_SIZE - log_count;
    
    va_start(argptr, fmt);
    count = vsnprintf(buff, count, fmt, argptr);
    va_end(argptr);

    buff[count] = 0;
    buff[sizeof(buff) - 1] = 0;

    for (i = 0; i < count && buff[i]; i++)
    {
        if (insert_ch(buff[i]))
            break;
    }

    wakeup_log_thread();
}

void create_log_thread(void)
{
    ccb_log = thread_create(log_thread, NULL, THREAD_PRIO_MID);
}

void colog_poll(char *fmt, ...)
{
    int i;
    va_list argptr;
    char buff[LOG_BUFFER_SIZE];
    int count = sizeof(buff) - 1;
    
    va_start(argptr, fmt);
    count = vsnprintf(buff, count, fmt, argptr);
    va_end(argptr);

    for (i = 0; i < count; i++)
    {
        if (buff[i] == '\n')
        {
            //while(!write_completed());
            write_ch('\r');
        }

        //while(!write_completed());
        write_ch(buff[i]);
    }
}

